// wtkwidget.hpp
//

#ifndef _WTKWIDGET_HPP
#define _WTKWIDGET_HPP

#include "wtk/wtkbase.hpp"
#include "wtk/wtkevent.hpp"

namespace wtk
{
  class Display;
  class Layout;



  // SizeRequest
  //
  struct WTK_API SizeRequest
  {
    int width;
    int height;
  };



  // Allocation
  //
  struct WTK_API Allocation
  {
    int x;
    int y;
    int width;
    int height;
  };



  // DrawContext
  //
  struct WTK_API DrawContext
  {
    Display &display;
    GLsizei viewport_width;
    GLsizei viewport_height;
    GLsizei root_x;
    GLsizei root_y;
    GLsizei clip_width;
    GLsizei clip_height;

    DrawContext () = delete;
    
    DrawContext ( Display &display,
                  GLsizei root_x,
                  GLsizei root_y,
                  GLsizei clip_width,
                  GLsizei clip_height )
      : display(display),
        viewport_width(clip_width),
        viewport_height(clip_height),
        root_x(root_x),
        root_y(root_y),
        clip_width(clip_width),
        clip_height(clip_height)
    {
    }

    DrawContext ( const DrawContext &chain,
                  GLsizei x,
                  GLsizei y,
                  GLsizei width,
                  GLsizei height )
      : display(chain.display),
        viewport_width(chain.viewport_width),
        viewport_height(chain.viewport_height),
        root_x(chain.root_x + x),
        root_y(chain.root_y + y),
        clip_width(std::min(width, chain.clip_width - x)),
        clip_height(std::min(height, chain.clip_height - y))
    {
    }
  };



  // WidgetFlagsBit
  //
  enum WidgetFlagsBit : size_t
    {
      bDestroyed = 0,
      bTopWidget,
      bNeedsResize,
      bVisible,
      bStaticLayout,
      _bBitCount,
    };



  // Widget
  //
  class WTK_API Widget: public IEventListener
  {
    friend Display;

    enum class TraverseValue
      {
        vContinue,
        vSkip,
        vStop,
      };

    using TraverseFunc = std::function<TraverseValue (Widget &)>;

  public:
    Widget ( Display &display );
    Widget ( Widget *parent );
    virtual ~Widget ();

    Widget ( Widget & ) = delete;
    Widget ( Widget && ) = delete;
    
    virtual void dummy () {}

    virtual Display *get_display () { return parent ? parent->get_display() : nullptr; }
    Widget *get_top_widget () { return is_top_widget() ? this : (parent ? parent->get_top_widget() : nullptr); }
    
    bool is_destroyed () { return flags.test(WidgetFlagsBit::bDestroyed); }
    bool is_top_widget () { return flags.test(WidgetFlagsBit::bTopWidget); }
    bool needs_resize () { return flags.test(WidgetFlagsBit::bNeedsResize); }
    bool is_visible () { return flags.test(WidgetFlagsBit::bVisible); }
    bool static_layout () { return flags.test(WidgetFlagsBit::bStaticLayout); }

    void destroy ();
    
    void add_child ( Widget &child );
    void remove_child ( Widget &child );
    void traverse ( TraverseFunc func );

    auto begin () { return children.begin(); }
    auto end () { return children.end(); }
    auto rbegin () { return children.rbegin(); }
    auto rend () { return children.rend(); }
    
    void show_all ();
    void show ();
    void hide ();
    
    void queue_resize ();

    void set_layout ( Layout &layout,
                      bool static_ = true);
    void reset_layout ();

    void set_focus ();

    void size_request ( SizeRequest &req );
    void size_allocate ( const Allocation &alloc );

    virtual void event ( const Event &event );
    virtual void event_keyboard ( const Event &event );
    virtual void event_mouse ( const Event &event );

    virtual void draw ( const DrawContext &context ) {}

    Widget *get_child_at ( int x,
                           int y,
                           int *rx,
                           int *ry );
    
  protected:
    virtual void on_size_request ( SizeRequest &req ) {}
    virtual void on_size_allocate ( const Allocation &alloc ) {}
    
  private:
    Layout *layout{nullptr};
    SizeRequest request{};

  protected:
    std::bitset<WidgetFlagsBit::_bBitCount> flags{};
    
    // [FIXME] not sure how to handle these
    int x{0};
    int y{0};
    int width{0};
    int height{0};

    Widget *parent{nullptr};
    std::list<Widget *> children{};

    void draw_child ( const DrawContext &context );

  private:
    TraverseValue real_traverse ( TraverseFunc func );
  };
}

#endif
